/* eslint-disable no-prototype-builtins */
const Promise = require('bluebird')

/**
 * @param {Object}              [query={}]
 * @param {Object}              [options={}]
 * @param {Object|String}         [options.select]
 * @param {Object|String}         [options.sort]
 * @param {Object|String}         [options.customLabels]
 * @param {Object|}               [options.collation]
 * @param {Array|Object|String}   [options.populate]
 * @param {Boolean}               [options.lean=false]
 * @param {Boolean}               [options.leanWithId=true]
 * @param {Number}                [options.offset=0] - Use offset or page to set skip position
 * @param {Number}                [options.page=1]
 * @param {Number}                [options.limit=10]
 * @param {Function}            [callback]
 *
 * @returns {Promise}
 */
function paginate(query, options, callback) {
    query = query || {}
    options = Object.assign({}, paginate.options, options)
    options.customLabels = options.customLabels ? options.customLabels : {}

    const select = options.select
    const sort = options.sort
    const collation = options.collation
    const populate = options.populate
    const lean = options.lean || false
    const leanWithId = options.hasOwnProperty('leanWithId') ? options.leanWithId : true

    const limit = options.hasOwnProperty('limit') ? options.limit : 10
    let skip
    let offset
    let page

    // Custom Labels
    const labelTotal = 'total'
    const labelLimit = 'pageSize'
    const labelPage = 'current'
    const labelPagination = 'pagination'
    const labelTotalPages = 'pages'
    const labelDocs = 'list'
    const labelNextPage = 'next'
    const labelPrevPage = 'prev'

    if (options.hasOwnProperty('offset')) {
        offset = options.offset
        skip = offset
    } else if (options.hasOwnProperty('page')) {
        page = options.page
        skip = (page - 1) * limit
    } else {
        offset = 0
        page = 1
        skip = offset
    }

    const promises = {
        docs: Promise.resolve([]),
        count: this.countDocuments(query).exec()
    }

    if (limit) {
        query = this.find(query)
            .select(select)
            .sort(sort)
            .collation(collation)
            .skip(skip)
            .limit(limit)
            .lean(lean)

        if (populate) {
            ;[].concat(populate).forEach(function(item) {
                query.populate(item)
            })
        }

        promises.docs = query.exec()

        if (lean && leanWithId) {
            promises.docs = promises.docs.then(function(docs) {
                docs.forEach(function(doc) {
                    doc.id = String(doc._id)
                })

                return docs
            })
        }
    }

    return Promise.props(promises)
        .then(function(data) {
            const result = {
                [labelDocs]: data.docs,
                [labelPagination]: {
                    [labelTotal]: data.count,
                    [labelLimit]: limit
                }
            }

            if (offset !== undefined) {
                result[labelPagination].offset = offset
            }

            if (page !== undefined) {
                const pages = Math.ceil(data.count / limit) || 1

                result.has_prev = false
                result.has_next = false

                result[labelPagination][labelPage] = page
                result[labelPagination][labelTotalPages] = pages

                // Set prev page
                if (page > 1) {
                    result.has_prev = true
                    result[labelPagination][labelPrevPage] = page - 1
                } else {
                    result[labelPagination][labelPrevPage] = undefined
                }

                // Set next page
                if (page < pages) {
                    result.has_next = true
                    result[labelPagination][labelNextPage] = page + 1
                } else {
                    result[labelPagination][labelNextPage] = undefined
                }
            }

            return result
        })
        .asCallback(callback)
}

/**
 * @param {Schema} schema
 */
module.exports = function(schema) {
    schema.statics.paginate = paginate
}

module.exports.paginate = paginate
